前幾天我們把系統從單一 Server 擴展成:
┌── Server #1
│
User → Load Balancer ├── Server #2
│
└── Server #3
↓
Database
Backend 可以 Horizontal Scaling,但所有 Server 最後仍然需要存取資料。
例如:
Customer
Product
Shopping Cart
Order
Payment
這時候就會遇到另一個重要問題:
這些資料到底應該怎麼存?
最常看到的兩個方向就是:
SQL
vs
NoSQL
今天除了 SQL / NoSQL,也會加入一組很適合台積電 IT Software Engineer 面試準備的情境:
設計 Customer、Product、Shopping Cart 與 Checkout。
SQL Database 通常指 Relational Database。
常見例子:
PostgreSQL
MySQL
MariaDB
Oracle
SQL Server
Relational Database 的核心特色之一是資料以 Table 組織,而且不同 Table 可以建立 Relationship。
例如:
users
| id | name | |
|---|---|---|
| 1 | Alvin | alvin@example.com |
| 2 | Bob | bob@example.com |
另一張 Table:
orders
| id | user_id | total_amount |
|---|---|---|
| 101 | 1 | 1200 |
| 102 | 1 | 800 |
其中:
orders.user_id → users.id
形成:
User
│
└── has many → Orders
例如:
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(255)
);
id 是 Primary Key,用來唯一識別 User。
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT,
total_amount DECIMAL(10, 2),
FOREIGN KEY (user_id) REFERENCES users(id)
);
user_id 則是 Foreign Key。
因此 SQL Database 很適合處理具有明確 Relationship 的資料。
假設想取得 Alvin 的 Orders:
SELECT
users.name,
orders.id,
orders.total_amount
FROM users
JOIN orders
ON users.id = orders.user_id
WHERE users.id = 1;
Relationship 可能一路延伸:
Users
↓
Orders
↓
Order Items
↓
Products
這種 Data Model 在 E-commerce、Banking、ERP、Inventory System 都非常常見。
NoSQL 並不是單一種類的 Database。
常見 Data Model:
Document
Key-Value
Wide-Column
Graph
例如:
MongoDB → Document
Redis → Key-Value / Data Structure Store
Cassandra → Wide-Column
Neo4j → Graph
以 Document Database 為例:
{
"_id": 1,
"name": "Alvin",
"email": "alvin@example.com",
"preferences": {
"language": "zh-TW",
"theme": "dark"
}
}
Document Model 在某些資料結構經常變動、欄位差異大的情境下會比較自然。
| SQL | NoSQL | |
|---|---|---|
| Data Model | Table / Row | Document、Key-Value 等 |
| Schema | 通常較明確 | 依 Database 類型而定 |
| Relationship | 很擅長 | 視資料模型而定 |
| JOIN | 常見 | 不一定支援或不鼓勵大量 JOIN |
| Transaction | 成熟且常見 | 能力依 Database 而不同 |
| Scaling | Vertical / Horizontal 都可 | 很多產品強調 Distributed Scaling |
但不要把它背成:
SQL = 小系統
NoSQL = 大系統
大型系統同樣可以使用 SQL。
SQL Database 也能搭配:
Index
Read Replica
Partitioning
Sharding
Caching
處理大量資料與 Traffic。
所以 Database Selection 應該考慮:
Data Model?
Relationship?
Query Pattern?
Read / Write Ratio?
Transaction Requirement?
Consistency Requirement?
Expected Scale?
假設面試官說:
請設計一個 Shopping System Database,包含 Customer、Product 和 Shopping Cart。
不要馬上畫 Table。
先確認 Requirement:
一個 Customer 可以有幾個 Active Cart?
一個 Cart 可以有多少 Product?
同一 Product 可以出現在不同 Cart 嗎?
需要 Quantity 嗎?
Product Price 會改變嗎?
Checkout 後 Cart 怎麼處理?
System Design 很重要的習慣:
Requirement → Design
CREATE TABLE customers (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
);
CREATE TABLE products (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
stock INT NOT NULL
);
CREATE TABLE carts (
id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
status VARCHAR(20) NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
例如:
ACTIVE
CHECKED_OUT
一個 Cart 可以有很多 Product,一個 Product 也可以出現在很多 Cart。
這是:
Many-to-Many Relationship
所以建立中間 Table:
CREATE TABLE cart_items (
cart_id BIGINT,
product_id BIGINT,
quantity INT NOT NULL,
PRIMARY KEY (cart_id, product_id),
FOREIGN KEY (cart_id) REFERENCES carts(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
Relationship:
Customer
↓
Cart
↓
Cart Items
↓
Product
User 按下 Checkout 時可能需要:
1. 確認 Cart
2. 確認 Inventory
3. 建立 Order
4. 建立 Order Items
5. 扣除 Inventory
6. 更新 Cart Status
概念上:
BEGIN TRANSACTION
Check Inventory
Create Order
Create Order Items
Update Inventory
Update Cart
COMMIT
如果:
Create Order ✅
Update Inventory ❌
我們不希望留下半完成的資料。
這就需要:
Transaction
A → Atomicity
C → Consistency
I → Isolation
D → Durability
一組 Transaction:
要嘛全部成功,要嘛全部失敗。
全部成功 → COMMIT
任何重要步驟失敗 → ROLLBACK
Transaction 前後都要維持 Database 定義的有效規則與 Constraint。
例如 Business Rule 不允許:
stock < 0
就不應該留下:
stock = -1
假設:
Product A stock = 1
Alvin 和 Bob 同時 Checkout:
Alvin Transaction ─┐
├── Product A
Bob Transaction ───┘
如果兩個 Transaction 同時看到:
stock = 1
兩個人都可能以為自己買得到。
這可能造成:
Overselling
這就是 Concurrent Transaction 與 Isolation 要處理的問題之一。
Transaction 成功 Commit 後,資料應該被可靠保存。
Order COMMIT
↓
Server Crash
↓
已 Commit 的 Order 不應隨意消失
這是一個很值得練習的問題:
Product 只剩最後一個,兩個 Customer 同時 Checkout 怎麼辦?
不能只寫:
if stock > 0:
stock -= 1
因為可能發生:
Initial Stock = 1
Transaction A reads 1
Transaction B reads 1
A → Buy
B → Buy
這就是:
Race Condition / Concurrency Problem
可能需要考慮:
Database Lock
Atomic Update
Optimistic Locking
Pessimistic Locking
Isolation Level
例如概念上可以:
UPDATE products
SET stock = stock - 1
WHERE id = 101
AND stock > 0;
再檢查:
Affected Rows = 1 → 成功
Affected Rows = 0 → 沒有庫存
這可以避免某些:
SELECT stock
↓
Application 判斷
↓
UPDATE stock
產生的 Read-Modify-Write Race Condition。
一個 Checkout 可以一路延伸:
Database Schema
↓
Relationship
↓
Transaction
↓
ACID
↓
Concurrency
↓
Isolation
↓
Locking
↓
High Traffic
↓
Database Bottleneck
真正需要練習的不是只背:
SQL = Relational
NoSQL = Non-relational
而是:
從 Requirement 一步一步發現系統問題,並解釋自己的 Solution 與 Trade-off。
例如不同 Product 的 Metadata 差異很大:
{
"type": "laptop",
"cpu": "M3",
"ram": "16GB"
}
另一種商品:
{
"type": "shirt",
"size": "L",
"material": "cotton"
}
Document Database 在這類彈性 Data Model 中可能比較自然。
另外:
session:abc123
↓
User Session
這種 Key → Value Access Pattern 則可能適合 Key-Value Store。
所以不是:
哪個 Database 比較強?
而是:
哪個 Data Model 最符合 Requirement 與 Access Pattern?
可以。
例如:
Orders
Payments
Inventory
↓
PostgreSQL
因為重視:
Transaction
Relationship
Consistency
而:
Session
Cache
↓
Redis
或:
Flexible Product Metadata
↓
Document Database
大型系統可能根據不同 Requirement 使用不同 Storage Technology。
如果面試官問:
你會選 SQL 還是 NoSQL?
先思考:
Data Model?
Relationship?
Query Pattern?
Read / Write Ratio?
Transaction Requirement?
Consistency Requirement?
Expected Scale?
然後再說明:
Based on these requirements,
I would start with PostgreSQL because...
重點不是猜面試官想聽哪個 Database。
而是:
說明你的選擇,以及 Trade-off。
仍然回到這個 Framework:
Problem
↓
Requirement
↓
Solution
↓
Trade-off
今天可以直接練習:
1. 請設計 Customer、Product、Shopping Cart 的 Database Schema。
2. Customer 和 Cart 是什麼 Relationship?
3. Cart 和 Product 是什麼 Relationship?
4. 為什麼需要 cart_items?
5. Checkout 時需要哪些 Database Operations?
6. 什麼是 Transaction?
7. ACID 分別代表什麼?
8. Checkout 中途失敗怎麼辦?
9. 只剩最後一個 Product,
兩個 User 同時 Checkout 怎麼辦?
10. Isolation 解決什麼問題?
11. 這個系統會選 SQL 還是 NoSQL?
Why?
12. Traffic 增加後 Database 變成 Bottleneck,
下一步會怎麼處理?
如果能不看答案,用自己的話完整講出這些問題,今天就同時完成:
鐵人賽 Day 5
+
System Design 基礎
+
台積電 IT 面試複習
SQL 很適合處理:
Structured Data
Relationship
JOIN
Transaction
Data Integrity
NoSQL 則包含:
Document
Key-Value
Wide-Column
Graph
選擇 Database 時,不要只看資料量。
應該從:
Requirement
Data Model
Query Pattern
Transaction
Consistency
Scale
一起考慮。
另外,Shopping Cart / Checkout 讓我們開始接觸:
Transaction
ACID
Concurrency
Isolation
Race Condition
這些觀念之後還會繼續出現在更完整的 System Design 裡。
當資料量從:
1,000 rows
↓
1,000,000 rows
↓
100,000,000 rows
Query 可能開始變慢。
例如:
SELECT *
FROM users
WHERE email = 'alvin@example.com';
Database 要怎麼快速找到資料?
下一篇:
Day 6|Database Index:為什麼加一個 Index,Query 可以快這麼多?
會開始了解:
Full Table Scan
Index
B-Tree
Read Performance
Write Cost
Composite Index
並延伸:
為什麼 Database Query 很慢?
怎麼找 Database Bottleneck?
什麼情況應該加 Index?
為什麼不能每個 Column 都加 Index?
再一步一步進入:
Database Optimization
Cache
Read Replica
Sharding